/*****************************************************************************
 * Copyright (C) 2009 The Forget3D Project by Martin Foo (i25ffz@hotmail.com)
 * ALL RIGHTS RESERVED
 *
 * License I
 * Permission to use, copy, modify, and distribute this software for
 * any purpose and WITHOUT a fee is granted under following requirements:
 * - You make no money using this software.
 * - The authors and/or this software is credited in your software or any
 *   work based on this software.
 *
 * Licence II
 * Permission to use, copy, modify, and distribute this software for
 * any purpose and WITH a fee is granted under following requirements:
 * - As soon as you make money using this software, you have to pay a
 *   licence fee. Until this point of time, you can use this software
 *   without a fee.
 * 	 Please contact Martin Foo (i25ffz@hotmail.com) for further details.
 * - The authors and/or this software is credited in your software or any
 *   work based on this software.
 *
 * THE MATERIAL EMBODIED ON THIS SOFTWARE IS PROVIDED TO YOU "AS-IS"
 * AND WITHOUT WARRANTY OF ANY KIND, EXPRESS, IMPLIED OR OTHERWISE,
 * INCLUDING WITHOUT LIMITATION, ANY WARRANTY OF MERCHANTABILITY OR
 * FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT SHALL THE AUTHORS
 * BE LIABLE TO YOU OR ANYONE ELSE FOR ANY DIRECT, SPECIAL, INCIDENTAL,
 * INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND, OR ANY DAMAGES WHATSOEVER,
 * INCLUDING WITHOUT LIMITATION, LOSS OF PROFIT, LOSS OF USE, SAVINGS OR
 * REVENUE, OR THE CLAIMS OF THIRD PARTIES, WHETHER OR NOT THE AUTHORS HAVE
 * BEEN ADVISED OF THE POSSIBILITY OF SUCH LOSS, HOWEVER CAUSED AND ON
 * ANY THEORY OF LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE
 * POSSESSION, USE OR PERFORMANCE OF THIS SOFTWARE.
 *****************************************************************************/


#include "model_am.h"

#define DEBUG 1


namespace F3D {
    /**
     * ModelMD2 class for all games using F3D.
     */

    ModelAM::ModelAM() :
        m_model(NULL) {

    }

    ModelAM::~ModelAM() {

        int i = 0;

        if (NULL == m_model)
            return;

        if (m_model->mesh_num == 0)
            return;

        for (i = 0; i < m_model->mesh_num; i++) {
            if (m_model->meshes[i].vertex_num > 0 && m_model->meshes[i].vertices != NULL)
                FREEANDNULL (m_model->meshes[i].vertices);

            if (m_model->meshes[i].triangle_num> 0 && m_model->meshes[i].triangles != NULL)
                FREEANDNULL (m_model->meshes[i].triangles);

         }

        FREEANDNULL(m_model->meshes);
    }


/*
    bool ModelAM::loadModelTest (const char *filename) {



        return true;
    }
*/
    bool ModelAM::loadModel(const char *filename) {

        FILE *file;
        int r, i, j;
        int version = 0;
        int mesh_num = 0;
        int vertex_num = 0;
        int uv_num = 0;
        int triangle_num = 0;
        int material_num = 0;
        int temp = 0;
        float t1, t2, t3;
        float specular_level, glossiness;

        file = fopen (filename, "r");
        if (file == NULL)
            return false;

        r = fscanf (file, "SimpleMeshFormat %d\n", &version);
        printf ("verson = %d.\n", version);

        r = fscanf (file, "%d\n", &mesh_num);
        printf ("mesh_num = %d.\n", mesh_num);

        setMeshCount (mesh_num);

        if(m_model == NULL)
            m_model = (am_model_t*)malloc(sizeof(am_model_t));

        m_model->mesh_num = mesh_num;
        m_model->meshes = (am_mesh_t*)malloc (mesh_num * sizeof (am_mesh_t));

        for (i = 0; i < mesh_num; i++) {

            r = fscanf (file, "%s\n", m_model->meshes[i].mesh_name);
            printf ("mesh_name = %s.\n", m_model->meshes[i].mesh_name);

            r = fscanf (file, "%d\n", &vertex_num);

            m_model->meshes[i].vertex_num = vertex_num;
            printf ("vertex_num = %d.\n", m_model->meshes[i].vertex_num);

            m_model->meshes[i].vertices = (am_vertex_t*)malloc (vertex_num * sizeof (am_vertex_t));
            if (!m_model->meshes[i].vertices) {
                //Fixme: free sth if needed.
                return false;
            }

            for (j = 0; j < vertex_num; j++)  {
                r = fscanf (file, "%g %g %g\n",
                                &m_model->meshes[i].vertices[j].vertex[0],
                                &m_model->meshes[i].vertices[j].vertex[1],
                                &m_model->meshes[i].vertices[j].vertex[2]);
                printf ("mesh[%d]->vertex[%d] = %g %g %g.\n", i, j,
                                m_model->meshes[i].vertices[j].vertex[0],
                                m_model->meshes[i].vertices[j].vertex[1],
                                m_model->meshes[i].vertices[j].vertex[2]);
            }

            r = fscanf (file, "%d\n", &uv_num);

            for (j = 0; j < uv_num; j++)  {
                r = fscanf (file, "%g %g\n",
                                &m_model->meshes[i].vertices[j].uvs[0],
                                &m_model->meshes[i].vertices[j].uvs[1]);
            }


            r = fscanf (file, "%d\n", &triangle_num);

            setTriangleNums (triangle_num, i);

            m_model->meshes[i].triangle_num = triangle_num;
            printf ("triangle_num = %d.\n", m_model->meshes[i].triangle_num);

            m_model->meshes[i].triangles = (am_triangle_t*)malloc (triangle_num * sizeof (am_triangle_t));
            if (!m_model->meshes[i].triangles) {
                //Fixme: free sth
                return false;
            }

            for (j = 0; j < triangle_num; j++)  {
                r = fscanf (file, "%d %d %d %d %g %g %g %g %g %g %g %g %g %g %g %g %g %g %g\n",
                               &temp, /*nSmoothGroup*/
                               &m_model->meshes[i].triangles[j].indices[0],
                               &m_model->meshes[i].triangles[j].indices[1],
                               &m_model->meshes[i].triangles[j].indices[2],
                               &m_model->meshes[i].triangles[j].normals[0][0],
                               &m_model->meshes[i].triangles[j].normals[0][1],
                               &m_model->meshes[i].triangles[j].normals[0][2],
                               &m_model->meshes[i].triangles[j].normals[1][0],
                               &m_model->meshes[i].triangles[j].normals[1][1],
                               &m_model->meshes[i].triangles[j].normals[1][2],
                               &m_model->meshes[i].triangles[j].normals[2][0],
                               &m_model->meshes[i].triangles[j].normals[2][1],
                               &m_model->meshes[i].triangles[j].normals[2][2],
                               &m_model->meshes[i].triangles[j].s[0],
                               &m_model->meshes[i].triangles[j].t[0],
                               &m_model->meshes[i].triangles[j].s[1],
                               &m_model->meshes[i].triangles[j].t[1],
                               &m_model->meshes[i].triangles[j].s[2],
                               &m_model->meshes[i].triangles[j].t[2]);
                /*
                printf ("%d %d %d %d %d %g %g %g %g %g %g %g %g %g %g %g %g %g %g %g\n",
                               j,
                               temp, //nSmoothGroup
                               m_model->meshes[i].triangles[j].indices[0],
                               m_model->meshes[i].triangles[j].indices[1],
                               m_model->meshes[i].triangles[j].indices[2],
                               m_model->meshes[i].triangles[j].normals[0][0],
                               m_model->meshes[i].triangles[j].normals[0][1],
                               m_model->meshes[i].triangles[j].normals[0][2],
                               m_model->meshes[i].triangles[j].normals[1][0],
                               m_model->meshes[i].triangles[j].normals[1][1],
                               m_model->meshes[i].triangles[j].normals[1][2],
                               m_model->meshes[i].triangles[j].normals[2][0],
                               m_model->meshes[i].triangles[j].normals[2][1],
                               m_model->meshes[i].triangles[j].normals[2][2],
                               m_model->meshes[i].triangles[j].s[0],
                               m_model->meshes[i].triangles[j].s[1],
                               m_model->meshes[i].triangles[j].s[2],
                               m_model->meshes[i].triangles[j].t[0],
                               m_model->meshes[i].triangles[j].t[1],
                               m_model->meshes[i].triangles[j].t[2]);  */

            }

            r = fscanf (file, "%d\n", &material_num);

            for (j = 0; j < material_num; j++)  {
                r = fscanf (file, "%g %g %g %g %g %g %g %g %g %g %g %g %g %g\n",
                               &specular_level,
                               &glossiness,
                               &m_model->meshes[i].ambient[0],
                               &m_model->meshes[i].ambient[1],
                               &m_model->meshes[i].ambient[2],
                               &m_model->meshes[i].diffuse[0],
                               &m_model->meshes[i].diffuse[1],
                               &m_model->meshes[i].diffuse[2],
                               &m_model->meshes[i].specular[0],
                               &m_model->meshes[i].specular[1],
                               &m_model->meshes[i].specular[2],
                               &m_model->meshes[i].emission[0],
                               &m_model->meshes[i].emission[1],
                               &m_model->meshes[i].emission[2]);

                if (specular_level > 0)
                    m_model->meshes[i].shininess[0] = glossiness;
                else
                    m_model->meshes[i].shininess[0] = -1;


                printf ("%g %g %g %g %g %g %g %g %g %g %g %g %g\n",
                               &m_model->meshes[i].shininess[0],
                               &m_model->meshes[i].ambient[0],
                               &m_model->meshes[i].ambient[1],
                               &m_model->meshes[i].ambient[2],
                               &m_model->meshes[i].diffuse[0],
                               &m_model->meshes[i].diffuse[1],
                               &m_model->meshes[i].diffuse[2],
                               &m_model->meshes[i].specular[0],
                               &m_model->meshes[i].specular[1],
                               &m_model->meshes[i].specular[2],
                               &m_model->meshes[i].emission[0],
                               &m_model->meshes[i].emission[1],
                               &m_model->meshes[i].emission[2]);

            }

        }

        fclose (file);

        return true;
    }

    void ModelAM::loadModel(am_model_t *model){

        int mesh_num = 0;
        int triangle_num = 0;
        int i = 0;

        if (m_model == NULL){

            m_model = model;

            mesh_num = model->mesh_num;
            setMeshCount (mesh_num);

            for (i = 0; i < mesh_num; i++) {
                triangle_num = model->meshes[i].triangle_num;
                setTriangleNums (triangle_num, i);
            }

        }

        return;

    }


    void ModelAM::updateModel(am_model_t *model){

        int mesh_num = 0;
        int triangle_num = 0;
        int i = 0;

        m_model = model;

        mesh_num = model->mesh_num;
        setMeshCount (mesh_num);

        for (i = 0; i < mesh_num; i++) {
            triangle_num = model->meshes[i].triangle_num;
            setTriangleNums (triangle_num, i);
        }

        return;

    }


    void ModelAM::prepareFrame() {

        int i = 0, j = 0;
        int vertex_id = 0;
        int uv_id = 0;
        int index_id = 0;
        int vertex_sum = 0;
        int triangle_sum = 0;
        int uv_sum = 0;

        if (m_model == NULL)
            return;

        for (i = 0; i < m_model->mesh_num; i++) {

            vertex_sum = m_model->meshes[i].vertex_num;
            uv_sum = m_model->meshes[i].vertex_num;
            triangle_sum = m_model->meshes[i].triangle_num;

            //printf ("vertex_sum = %d, triangle_sum = %d.\n", vertex_sum, triangle_sum);

            float *vertices = (float*)malloc (vertex_sum * 3 * sizeof (float));
            short *indices = (short *)malloc (triangle_sum * 3 * sizeof (short));
            float *uvs = (float*)malloc (uv_sum * 2 * sizeof (float));

            vertex_id = 0;
            for (j = 0; j < m_model->meshes[i].vertex_num; j++){

                vertices [vertex_id++] = m_model->meshes[i].vertices[j].vertex[0];
                vertices [vertex_id++] = m_model->meshes[i].vertices[j].vertex[1];
                vertices [vertex_id++] = m_model->meshes[i].vertices[j].vertex[2];

            }

            uv_id = 0;
            for (j = 0; j < m_model->meshes[i].vertex_num; j++){

                uvs [uv_id++] = m_model->meshes[i].vertices[j].uvs[0];
                uvs [uv_id++] = m_model->meshes[i].vertices[j].uvs[1];

            }


            //printf ("final vertex_id = %d.\n", vertex_id);

            index_id = 0;
            for (j = 0; j < m_model->meshes[i].triangle_num; j++){
                indices [index_id++] = m_model->meshes[i].triangles[j].indices[0];
                indices [index_id++] = m_model->meshes[i].triangles[j].indices[1];
                indices [index_id++] = m_model->meshes[i].triangles[j].indices[2];
            }
            //printf ("index_id = %d.\n", index_id);



            setVertices (vertices, vertex_sum * 3 * sizeof (float), i);
            setIndices (indices, triangle_sum * 3 * sizeof (short), i);
            setUvs(uvs, uv_sum * 2 * sizeof (float), i);


            setMaterialAmbient (m_model->meshes[i].ambient[0],
                                m_model->meshes[i].ambient[1],
                                m_model->meshes[i].ambient[2],
                                i);

            setMaterialDiffuse (m_model->meshes[i].diffuse[0],
                                m_model->meshes[i].diffuse[1],
                                m_model->meshes[i].diffuse[2],
                                i);

            setMaterialEmission (m_model->meshes[i].emission[0],
                                m_model->meshes[i].emission[1],
                                m_model->meshes[i].emission[2],
                                i);

            if (m_model->meshes[i].shininess[0] > 0) {

                setMaterialShininess (m_model->meshes[i].shininess[0], i);
                setMaterialSpecular (m_model->meshes[i].specular[0],
                                m_model->meshes[i].specular[1],
                                m_model->meshes[i].specular[2],
                                i);

            }

            FREEANDNULL (vertices);
            FREEANDNULL (indices);
            FREEANDNULL (uvs);
        }

        return;

    }
}
